#include "SWFTagWrapper.h"
#include "SWFTag.h"
#include "SWF.h"
#include "tags/TagDoABC.h"
#include <map>

std::map<SWFTag*, SWFTagWrapper*> _wrapperMap;

SWFTagWrapper::SWFTagWrapper( SWFTag* tag )
	: _tag(tag)
{
	_tag->retain();
	_wrapperMap[_tag] = this;
}

SWFTagWrapper::SWFTagWrapper()
	: _tag(nullptr)
{

}

SWFTagWrapper::~SWFTagWrapper()
{
	if (_tag)
		_tag->release();
	_tag = nullptr;
}

void SWFTagWrapper::replace( SWFTag* tag )
{
	SWFTag* old = _tag;
	_tag = tag;
	for (SWFTagWrapper* deps : dependents)
	{
		deps->onRefReplaced(old, tag);
	}
	old->release();
	tag->retain();
}

SWFTag* SWFTagWrapper::getTag() const
{
	return _tag;
}

SWFTagWrapper* SWFTagWrapper::create( SWFTag* tag )
{
	if (tag == nullptr)
		return nullptr;

	return new SWFTagWrapper(tag);
}

int SWFTagWrapper::getTagCode() const
{
	return _tag->code;
}

std::string SWFTagWrapper::getTagName()
{
	return _tag->getTagName();
}

void SWFTagWrapper::onRefReplaced( SWFTag* oldTag, SWFTag* newTag )
{
	SWFTagRef* tagRef = _tag->getReference(oldTag);
	tagRef->target = dynamic_cast<DefineTag*>(newTag);
}

SWFWrapper::SWFWrapper( SWF* swf )
{
	this->frameRate = swf->frameRate;
	this->width = swf->width;
	this->height = swf->height;

	this->fileAttributes = SWFTagWrapper::create(swf->fileAttributes);
	this->enableDebugger = SWFTagWrapper::create(swf->enableDebugger);
	this->enableDebugger2 = SWFTagWrapper::create(swf->enableDebugger2);
	this->metadata = SWFTagWrapper::create(swf->metadata);
	this->scriptLimits = SWFTagWrapper::create(swf->scriptLimits);
	this->setBackgroundColor = SWFTagWrapper::create(swf->setBackgroundColor);
	this->defineSceneAndFrameLabelData = SWFTagWrapper::create(swf->defineSceneAndFrameLabelData);
	this->productInfo = SWFTagWrapper::create(swf->productInfo);
	this->protect = SWFTagWrapper::create(swf->protect);

	std::vector<SWFFrame*> allFrames;
	swf->getAllFrames(allFrames);
	for (SWFFrame* frame : allFrames)
	{
		this->frames.push_back(new SWFFrameWrapper(frame));
	}

	for (SWFFrameWrapper* frameWrapper : frames)
	{
		frameWrapper->analyzeTagDependencies();
	}
}

#define ConvertTag(tag, wrapper, type) if (wrapper && wrapper->getTag()) tag = (type) wrapper->getTag()

SWF* SWFWrapper::convertToSWF()
{
	SWF* swf = new SWF();
	swf->frameRate = frameRate;
	swf->width = width;
	swf->height = height;

	ConvertTag(swf->fileAttributes, fileAttributes, TagFileAttributes*);
	ConvertTag(swf->metadata, metadata, TagMetadata*);
	ConvertTag(swf->scriptLimits, scriptLimits, TagScriptLimits*);
	ConvertTag(swf->setBackgroundColor, setBackgroundColor, TagSetBackgroundColor*);
	ConvertTag(swf->defineSceneAndFrameLabelData, defineSceneAndFrameLabelData, TagDefineSceneAndFrameLabelData*);
	ConvertTag(swf->productInfo, productInfo, TagProductInfo*);
	ConvertTag(swf->protect, protect, TagProtect*);
	ConvertTag(swf->enableDebugger, enableDebugger, TagEnableDebugger*);
	ConvertTag(swf->enableDebugger2, enableDebugger2, TagEnableDebugger2*);

	for (SWFFrameWrapper* frame : frames)
	{
		SWFFrame* f = frame->convertToFrame();
		swf->addFrame(f);
	}

	return swf;
}

void SWFWrapper::getImageTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getImageTags(output);
	}
}

void SWFWrapper::getShapeTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getShapeTags(output);
	}
}

void SWFWrapper::getSpriteTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getSpriteTags(output);
	}
}

void SWFWrapper::getFontTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getFontTags(output);
	}
}

void SWFWrapper::getTextTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getTextTags(output);
	}
}

void SWFWrapper::getButtonTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getButtonTags(output);
	}
}

void SWFWrapper::getOtherTags( std::vector<SWFTagWrapper*>& output ) const
{
	for (SWFFrameWrapper* frameWarpper : frames)
	{
		frameWarpper->getOtherTags(output);
	}
}

#define WarpTagVector(f, t) for (SWFTag* tag : f) t.push_back(SWFTagWrapper::create(tag));

SWFFrameWrapper::SWFFrameWrapper( SWFFrame* frame )
{
	this->frameLabel = SWFTagWrapper::create(frame->getFrameLable());
	this->symbolClass = SWFTagWrapper::create(frame->getSymbolClass());

	WarpTagVector(frame->controlTags, this->controlTags);
	WarpTagVector(frame->doABCs, this->doABCs);
	WarpTagVector(frame->exportDefs, this->exportDefs);
	WarpTagVector(frame->imports, this->imports);

	WarpTagVector(frame->buttons, this->buttons);
	WarpTagVector(frame->binaryDatas, this->binaryDatas);
	WarpTagVector(frame->bitmaps, this->bitmaps);
	WarpTagVector(frame->fonts, this->fonts);
	WarpTagVector(frame->morphShapes, this->morphShapes);
	WarpTagVector(frame->shapes, this->shapes);
	WarpTagVector(frame->sounds, this->sounds);
	WarpTagVector(frame->sprites, this->sprites);
	WarpTagVector(frame->texts, this->texts);
}

SWFFrameWrapper::~SWFFrameWrapper()
{
	
}

#define ConvertWrapperToTag(from, to, t) for (SWFTagWrapper* w : from) to.push_back(dynamic_cast<t>(w->getTag()));

SWFFrame* SWFFrameWrapper::convertToFrame()
{
	SWFFrame* frame = SWFFrame::create();
	if (this->frameLabel)
		frame->setFrameLabel(dynamic_cast<TagFrameLabel*>(this->frameLabel->getTag()));
	if (this->symbolClass)
		frame->setSymbolClass(dynamic_cast<TagSymbolClass*>(this->symbolClass->getTag()));

	ConvertWrapperToTag(this->controlTags, frame->controlTags, SWFTag*);
	ConvertWrapperToTag(this->doABCs, frame->doABCs, TagDoABC*);
	ConvertWrapperToTag(this->exportDefs, frame->exportDefs, DefineTag*);
	ConvertWrapperToTag(this->imports, frame->imports, TagImportAssets2*);

	ConvertWrapperToTag(this->buttons, frame->buttons, DefineTag*);
	ConvertWrapperToTag(this->binaryDatas, frame->binaryDatas, DefineTag*);
	ConvertWrapperToTag(this->bitmaps, frame->bitmaps, DefineTag*);
	ConvertWrapperToTag(this->fonts, frame->fonts, DefineTag*);
	ConvertWrapperToTag(this->morphShapes, frame->morphShapes, DefineTag*);
	ConvertWrapperToTag(this->shapes, frame->shapes, DefineTag*);
	ConvertWrapperToTag(this->sounds, frame->sounds, DefineTag*);
	ConvertWrapperToTag(this->sprites, frame->sprites, DefineTag*);
	ConvertWrapperToTag(this->texts, frame->texts, DefineTag*);
	return frame;
}

#define AnalyzeTagDependencies(vec) for (SWFTagWrapper* tagWrapper : vec)\
{\
	std::vector<SWFTagRef*> refs;\
	tagWrapper->getTag()->getReferences(refs);\
	for (SWFTagRef* r : refs)\
	{\
		SWFTagWrapper* refWrapper = _wrapperMap[r->target];\
		refWrapper->dependents.push_back(tagWrapper);\
	}\
}

void SWFFrameWrapper::analyzeTagDependencies()
{
	AnalyzeTagDependencies(this->controlTags);
	AnalyzeTagDependencies(this->exportDefs);
	AnalyzeTagDependencies(this->buttons);
	AnalyzeTagDependencies(this->binaryDatas);
	AnalyzeTagDependencies(this->bitmaps);
	AnalyzeTagDependencies(this->fonts);
	AnalyzeTagDependencies(this->morphShapes);
	AnalyzeTagDependencies(this->shapes);
	AnalyzeTagDependencies(this->sounds);
	AnalyzeTagDependencies(this->sprites);
	AnalyzeTagDependencies(this->texts);
}

void SWFFrameWrapper::getImageTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), bitmaps.begin(), bitmaps.end());
}

void SWFFrameWrapper::getShapeTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), shapes.begin(), shapes.end());
}

void SWFFrameWrapper::getSpriteTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), sprites.begin(), sprites.end());
}

void SWFFrameWrapper::getFontTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), fonts.begin(), fonts.end());
}

void SWFFrameWrapper::getTextTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), texts.begin(), texts.end());
}

void SWFFrameWrapper::getButtonTags( std::vector<SWFTagWrapper*>& output ) const
{
	output.insert(output.end(), buttons.begin(), buttons.end());
}

void SWFFrameWrapper::getOtherTags( std::vector<SWFTagWrapper*>& output ) const
{

}
